<template>
  <div
    class="mind-mapbox-editor-block"
    :style="editorStyle?.c1">
    <div
      class="mind-mapbox-contenteditable"
      ref="mindContentEditable"
      :data-placeholder="[PLACEHOLDER_TIP_MAP[editorTypeName]]"
      :style="editorStyle?.c2"
      contenteditable
      @keydown="bindEventlistenKeydown"
      @blur="handleEditorBlur"
      @input="handleEditorChange">{{ value }}</div>
  </div>
</template>

<script setup>
import { computed, ref, defineProps, defineEmits, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { resetcutKeydown, shortcutKeydown } from '../shortcut-key'
import { PLACEHOLDER_TIP_MAP } from '../config'

const emits = defineEmits(['set-editable-value', 'real-time-update-node'])
const props = defineProps({
  value: {
    type: String,
    default: ''
  },
  editorTypeName: String,
  subjectEditorStyle: {
    type: Object,
    default: null
  }
})

const mindContentEditable = ref(null)

/**
 * enter事件和ctrl + enter事件换行
 * @param {*} event
 */
function bindEventlistenKeydown (event) {
  if (event.ctrlKey && event.keyCode === 13) {
    event.preventDefault()
    event.stopPropagation()
    if (['link', 'tag', 'relation'].includes(props.editorTypeName)) return
    const docFragment = document.createDocumentFragment()
    const newEle = document.createElement('br')
    docFragment.appendChild(newEle)
    let range = window.getSelection().getRangeAt(0)
    range.deleteContents()
    if (!hasNextSibling(range.endContainer) && range.startOffset === range.startContainer.length) {
      const extrBreak = document.createElement('br')
      docFragment.appendChild(extrBreak)
    }
    range.insertNode(docFragment)
    range = document.createRange()
    range.setStartAfter(newEle)
    range.collapse(true)
    const sel = window.getSelection()
    sel.removeAllRanges()
    sel.addRange(range)
    handleEditorChange(event)
  } else if (event.keyCode === 13) {
    event.preventDefault()
    event.stopPropagation()
    const editValue = event.target.innerText.replace(/(^\s*)|(\s*$)/g, '')
    emits('set-editable-value', editValue, props.editorTypeName, editValue !== props.value)
  }
}

/**
 * 编辑主题文字的时候监听数据变化
 * @param {*} event
 */
function handleEditorChange (event) {
  if (event.target.innerHTML === '<br>') (event.target.innerHTML = '')
  if (props.editorTypeName === 'text') {
    emits('real-time-update-node', { ...props.subjectEditorStyle, text: event.target.innerText })
  }
}

function handleEditorBlur (event) {
  if (props.editorTypeName === 'text') {
    const id = props.subjectEditorStyle.id
    const editValue = event.target.innerText.replace(/(^\s*)|(\s*$)/g, '')
    emits('set-editable-value', editValue, props.editorTypeName, editValue !== props.value, id)
  }
}

function hasNextSibling (node) {
  if (node.nextElementSibling) return true
  while (node.nextSibling) {
    node = node.nextSibling
    if (node.length > 0) {
      return true
    }
  }
  return false
}

function setContentSelectPos () {
  const ele = document.querySelector('.mind-mapbox-contenteditable')
  const selection = window.getSelection()
  const range = document.createRange()
  if (!props.value.length) {
    range.selectNodeContents(ele)
    range.collapse(false)
  } else {
    range.setStart(ele.firstChild, 0)
    range.setEnd(ele.firstChild, props.value.length)
  }
  selection.removeAllRanges()
  selection.addRange(range)
}

nextTick(setContentSelectPos)

const editorStyle = computed(() => {
  if (props.subjectEditorStyle) {
    const { top, left, maxWidth, k, ...reset } = props.subjectEditorStyle
    return {
      c1: {
        top: `${top}px`,
        left: `${left}px`,
        maxWidth: 'none',
        minHeight: 'auto',
        lineHeight: 'inherit',
        padding: 0,
        boxShadow: 'none',
        background: 'transparent',
        transform: `scale(${k})`,
        transformOrigin: 'left top'
      },
      c2: {
        marginTop: 0,
        width: 'auto',
        maxWidth: `${maxWidth}px`,
        minWidth: '12px',
        minHeight: '12px',
        ...reset
      }
    }
  }
  return null
})

onMounted(() => {
  resetcutKeydown()
})

onBeforeUnmount(() => {
  shortcutKeydown()
})
</script>

<style lang="scss" scoped>
.mind-mapbox-editor-block {
  position: fixed;
  top: 60px;
  left: 50%;
  z-index: 10;
  background: #fff;
  box-shadow: 0 4px 15px #0000001a,0 1px 4px #0000000d;
  border-radius: 4px;
  min-height: 42px;
  line-height: 42px;
  padding: 12px;
  transform: scale(1) translate(-50%, 0);
  .mind-mapbox-contenteditable {
    color: #4c4c4c;
    font-size: 14px;
    outline: none;
    width: 420px;
    white-space: pre-wrap;
    word-break: break-all;
    line-break: anywhere;
    &:empty::before{
      content: attr(data-placeholder);
      color: 999;
      font-size: 12px;
    }
  }
}
</style>
